package org.cerberus.platform.internal.impl;

import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.cerberus.platform.internal.domain.IComponentClass;
import org.cerberus.platform.internal.domain.IComponentClassAttribute;
import org.cerberus.platform.internal.domain.IComponentClassEvent;
import org.cerberus.platform.internal.domain.IComponentClassResource;

public abstract class SystemClasses {

	//TODO check if necessary
	private final static Class<?>[] NATIVE_ARRAY_CLASSES = new Class<?>[] {
		int[].class, short[].class, long[].class, double[].class, byte[].class,
				boolean[].class, float[].class, char[].class, String[].class,
				Integer[].class, Long[].class, Short[].class, Double[].class,
				Byte[].class, Boolean[].class, Float[].class, Throwable[].class,
				Character[].class, Date[].class, File[].class, URL[].class,
				URI[].class, BigDecimal[].class, BigInteger[].class,
				Number[].class,
				// swing/awt classes
				Image[].class, Font[].class, Color[].class, };
	private static final List<Class<?>> CLASSES_ARRAY = Arrays.asList(NATIVE_ARRAY_CLASSES);
	
	public static final IComponentClass OBJECT_CLASS = createNativeClass(Object.class, false);
	public static final IComponentClass UNDEFINED_CLASS = createNativeClass(void.class, false);
	
	private final static IComponentClass[] OTHER = new IComponentClass[] {
		OBJECT_CLASS,
		UNDEFINED_CLASS,
	};
	
	final static SystemClass[] PRIMITIVES = new SystemClass[] {
		createNativeClass(int.class, true),
		createNativeClass(short.class, true),
		createNativeClass(long.class, true),
		createNativeClass(double.class, true),
		createNativeClass(byte.class, true),
		createNativeClass(boolean.class, true),
		createNativeClass(float.class, true),
		createNativeClass(char.class, true),
		createNativeClass(String.class, true),
		createNativeClass(Integer.class, true),
		createNativeClass(Long.class, true),
		createNativeClass(Short.class, true),
		createNativeClass(Double.class, true),
		createNativeClass(Byte.class, true),
		createNativeClass(Boolean.class, true),
		createNativeClass(Float.class, true),
		createNativeClass(Throwable.class, true),
		createNativeClass(Character.class, true),
		createNativeClass(Date.class, true),
		createNativeClass(File.class, true),
		createNativeClass(URL.class, true),
		createNativeClass(URI.class, true),
		createNativeClass(BigDecimal.class, true),
		createNativeClass(BigInteger.class, true),
		createNativeClass(Number.class, true),
		// swing/awt classes
		createNativeClass(Image.class, true),
		createNativeClass(Font.class, true),
		createNativeClass(Color.class, true),
		createNativeClass(Locale.class, true),
	};
	
	private static Map<Class<?>, SystemClass> PRIMITIVES_MAP;
	static {
		PRIMITIVES_MAP = new HashMap<Class<?>, SystemClass>();
		for (SystemClass primitive : PRIMITIVES) {
			PRIMITIVES_MAP.put(primitive.getJavaClass(), primitive);
		}
	}
	
	private static SystemClass createNativeClass(Class<?> nativeClass, boolean primitive) {
		return new SystemClass(nativeClass, createNativeClassID(nativeClass), primitive);
	}
	
	
	public static IComponentClass get(Class<?> nativeClass) {
		IComponentClass clazz = PRIMITIVES_MAP.get(nativeClass);
		if (clazz == null) {
			return OBJECT_CLASS;
		}
		return clazz;
	}
	
	private static String createNativeClassID(Class<?> clazz) {
		if (clazz.isPrimitive()) {
			// primitive types should have the same class id as
			// their object representations
			String simpleName = clazz.getSimpleName();
			String name = Character.toUpperCase(simpleName.charAt(0))+simpleName.substring(1, simpleName.length());
			return "system."+name;
		} else {
			return "system."+clazz.getSimpleName();
		}
	}
	
	public static boolean isNativeClassID(String classID) {
		return classID.startsWith("system.");
	}
	
	public static boolean isNativeClass(Class<?> clazz) {
		if (clazz.isPrimitive()) {
			return true;
		}
		return PRIMITIVES_MAP.containsKey(clazz);
	}
	
	public static boolean isNativeArray(Object[] array) {
		return CLASSES_ARRAY.contains(array.getClass());
	}
	
	public static Object getPrimitiveDefaultValue(Class<?> nativeType) {
		if (nativeType == Boolean.TYPE) {
			return false;
		} else if (nativeType == Character.TYPE) {
			return (char)0;
		} else if (nativeType == Byte.TYPE) {
			return (byte)0;
		} else if (nativeType == Integer.TYPE) {
			return 0;
		} else if (nativeType == Long.TYPE) {
			return 0L;
		} else if (nativeType == Float.TYPE) {
			return 0F;
		} else if (nativeType == Double.TYPE) {
			return 0D;
		} else if (nativeType == Short.TYPE) {
			return (short)0;
		}
		return null;
	}
	
	static class SystemClass implements IComponentClass {
		private String classID;
		private Class<?> clazz;
		private boolean primitive;
		public SystemClass(Class<?> clazz, String classID, boolean primitive) {
			this.classID = classID;
			this.clazz = clazz;
			this.primitive = primitive;
		}
		@Override
		public boolean isDeclared() {
			return primitive;
		}
		
		@Override
		public int getAttributeCount() {
			return 0;
		}
		
		@Override
		public IComponentClassAttribute getAttribute(String attributeName) {
			return null;
		}
		public Class<?> getJavaClass() {
			return clazz;
		}
		@Override
		public IComponentClassAttribute[] getAttributes() {
			return null;
		}
		@Override
		public String getClassID() {
			return classID;
		}
		@Override
		public IComponentClassAttribute[] getDeclaredAttributes() {
			return null;
		}
		@Override
		public IComponentClassEvent[] getDeclaredEvents() {
			return null;
		}
		@Override
		public IComponentClassResource[] getDeclaredResources() {
			return null;
		}
		@Override
		public IComponentClassEvent[] getEvents() {
			return null;
		}
		@Override
		public IComponentClassResource[] getResources() {
			return null;
		}

		@Override
		public IComponentClass getSuperClass() {
			return null;
		}

		@Override
		public boolean isRoot() {
			return false;
		}

		@Override
		public boolean isPrimitive() {
			return primitive;
		}
		
		@Override
		public boolean isArray() {
			return false;
		}
		@Override
		public String toString() {
			return classID;
		}
	}
	
}
